#pragma once
#include "game_chunk.h"
#include "game_entity_list.h"
#include "game_entity.h"
#include <string>
void destructable_set(Destructable * d, Uint8 hp, Uint8 itemID, Uint8 itemAmount )
{
	d->max_health = hp;
	d->cur_health = hp;
	d->itemID = itemID;
	d->itemAmount = itemAmount;
}

Chunk * game_get_chunk_unsafe(Chunk ** arry, V2i chunk_id)
{
	if (chunk_id.x < 0 || chunk_id.x >= G::CHUNK_COUNT ||
		chunk_id.y < 0 || chunk_id.y >=  G::CHUNK_COUNT 	){return 0;}
	
	Chunk * Result = arry[chunk_id.y *G::CHUNK_COUNT + chunk_id.x];
	if (Result)
	{
		return Result;
	}
	else
	{
		LOG_ERROR(__FUNCTION__);

		arry[chunk_id.y *G::CHUNK_COUNT + chunk_id.x] = game_chunk_create(chunk_id);
		assert(arry[chunk_id.y *G::CHUNK_COUNT + chunk_id.x] );
		return arry[chunk_id.y *G::CHUNK_COUNT + chunk_id.x];

	}
}

Chunk * game_get_chunk(Chunk * arry[G::CHUNK_COUNT][G::CHUNK_COUNT], V2i chunk_id)
{
	if (chunk_id.x < 0 || chunk_id.x >= G::CHUNK_COUNT ||
		chunk_id.y < 0 || chunk_id.y >=  G::CHUNK_COUNT 	)
	{
		//printf("chunk [%d,%d] accessed\n",chunk_id.x, chunk_id.y);
		return 0;
	}
	Chunk * Result = arry[chunk_id.y][chunk_id.x];
	if (Result)
	{
		return Result;
	}
	else
	{
		arry[chunk_id.y][chunk_id.x] = game_chunk_create(chunk_id);
		assert(arry[chunk_id.y][chunk_id.x] );
		return arry[chunk_id.y][chunk_id.x];
	}
}

//Chunk * game_get_chunk(Game * game, V2i chunk_id)
//{
//	return game_get_chunk(game->_chunks, chunk_id);
//}

V2i game_get_chunk_id(V2 abs_world_pos)
{
	return abs_world_pos * (1.0f / ((float)(G::GRAPHICS_TILE_SIZE * G::TILES_IN_CHUNK)));
}

Chunk * game_get_chunk_at_pos(Chunk * arry[G::CHUNK_COUNT][G::CHUNK_COUNT], V2 abs_world_pos)
{
	V2i chunk_id = game_get_chunk_id(abs_world_pos);
	assert(game_get_chunk(arry,chunk_id));
	return game_get_chunk(arry,chunk_id);
}



//remove this
V2 Xgame_get_abs_pos(V2i chunk_id, V2 offset)
{
	return ((V2)chunk_id * (float)G::CHUNK_SIZE_PIXELS) + offset;
}




int IsSet(const Chunk * c, V2i tile_index, Uint32 flags)
{
	return c->flags[tile_index.y][tile_index.x] & flags;
}


Chunk * game_chunk_create( V2i chunk_id)
{

	Chunk * chunk = new Chunk();
	chunk->id = chunk_id;
	chunk->entitys = new EntityList(chunk_id);

	//printf("Chunk %d, %d, created\n",chunk_id.x, chunk_id.y);

	for (int y = 0; y < G::TILES_IN_CHUNK; y++)
		for (int x = 0; x < G::TILES_IN_CHUNK; x++)
		{
			if(chunk)
			{
				chunk->flags[y][x] = TILE_DISABLED;
			}
		}

	for (int y = 0; y < G::TILES_IN_CHUNK; y++)
		for (int x = 0; x < G::TILES_IN_CHUNK; x++)
		{
			if (y%6 == 2 || y% 6 == 1  )
			{
				if (x % 7 != 0 )
				{
					chunk->flags[y][x] = TILE_VISIBLE | TILE_COLLISION | TILE_DESTRUCTABLE;
					chunk->sprite_index[y][x] = 1;
					chunk->destructable[y][x] = new Destructable();
					destructable_set(chunk->destructable[y][x],32,1,5);
					//int z = 0;
				}
				if (x%7 == 0)
				{
					chunk->flags[y][x] = TILE_VISIBLE | TILE_COLLISION | TILE_BELT;
					chunk->sprite_index[y][x] = 4;
				}
			}

	}
	return chunk;
}

int gc_iter = 0;

//points are relative to chunk position
//TODO: re-write this


 ChunkPortion get_nearby_tiles(Chunk * chunk_array[G::CHUNK_COUNT][G::CHUNK_COUNT], V2i initial_chunk_index, V2i initial_tile_index, V2i entity_size)
{
	//printf("CID = %d,%d \n",initial_chunk_index.x, initial_chunk_index.y);
	ChunkPortion Result = {};
	assert(math_values_in_range(initial_tile_index,v2i(0),v2i(G::TILES_IN_CHUNK)));
	assert(math_values_in_range(initial_chunk_index,v2i(0),v2i(G::CHUNK_COUNT)));
	

	V2i tile_overlap=entity_size / v2i(G::GRAPHICS_TILE_SIZE);
	
	//lets get min and max tile index			//define the range
	V2i min_tile_index = initial_tile_index ;
	V2i max_tile_index = initial_tile_index +tile_overlap+ v2i(2);

	V2i chunk_index = initial_chunk_index;
	V2i tile_index = initial_tile_index;
	Result.size = max_tile_index - min_tile_index;

	assert(Result.size.x <CHUNK_PORTION_SIZE);
	assert(Result.size.y < CHUNK_PORTION_SIZE);

	//lets get overlaps
	if (min_tile_index.x < 0 && chunk_index.x != 0)
	{
		assert(min_tile_index.x == -1);
		chunk_index.x --; 
		tile_index.x = G::TILES_IN_CHUNK -1;
	} 

	if (min_tile_index.y < 0 && chunk_index.y != 0)
	{
		assert(min_tile_index.y == -1);
		chunk_index.y --; 
		tile_index.y = G::TILES_IN_CHUNK -1;
	} 


	//min_tile_index = tile_index;
	V2i result_index = v2i(0,0);
	Position pos = {};
	position_set(chunk_index,min_tile_index * v2i(G::GRAPHICS_TILE_SIZE),pos);

	Result.top_left_world_pos = pos.world;
	//graphics_add_rect_to_stack(pos.world,v2i(G::GRAPHICS_TILE_SIZE),128,255,0,128);


	Chunk * cur_chunk = game_get_chunk(chunk_array,chunk_index);
	assert(cur_chunk);
	//printf("minRX = %d minRY = %d \n", min_tile_index.x,min_tile_index.y);
	V2i orig_min_tile_index = min_tile_index;
	V2i orig_max_tile_index = max_tile_index;
	//printf("Size = %d, %d \n",Result.size.x,Result.size.y);
	for (tile_index.y = min_tile_index.y; tile_index.y < max_tile_index.y; tile_index.y++)
	{
		result_index.x = 0;
		max_tile_index.x = orig_max_tile_index.x;
		chunk_index.x = initial_chunk_index.x;
		for (tile_index.x = min_tile_index.x; tile_index.x < max_tile_index.x; tile_index.x++)
		{
			if ( tile_index.x == G::TILES_IN_CHUNK )
			{
				if(game_get_chunk(chunk_array,chunk_index + v2i(1,0)))
				{
					tile_index.x -= G::TILES_IN_CHUNK; 
					chunk_index.x++;
					max_tile_index.x -= G::TILES_IN_CHUNK;
					cur_chunk = game_get_chunk(chunk_array,chunk_index);
				}
			}
			if ( tile_index.y == G::TILES_IN_CHUNK )
			{
				if(game_get_chunk(chunk_array,chunk_index + v2i(0,1)))
				{
					tile_index.y -= G::TILES_IN_CHUNK; 
					chunk_index.y++;
					max_tile_index.y -= G::TILES_IN_CHUNK;
					cur_chunk = game_get_chunk(chunk_array,chunk_index);
				}
			}

			position_set(chunk_index,tile_index * v2i(G::GRAPHICS_TILE_SIZE),pos);
			//printf("ci = [%d, %d] ti = [%d, %d] \n",chunk_index.x,chunk_index.y, tile_index.x, tile_index.y);  
			//graphics_add_rect_to_stack(pos.world,v2i(G::GRAPHICS_TILE_SIZE),255,128,0,8);

			assert(result_index.x <= Result.size.x);

			assert(result_index.y <= Result.size.y);
			Result.flags[result_index.y][result_index.x] =
				cur_chunk->flags[tile_index.y][tile_index.x];

			//printf("RX = %d RY = %d \n", result_index.x,result_index.y);
			result_index.x++;
		}
		result_index.y++;
	}
	if (result_index.x == 0)
	{
		
	}
	//printf("Result Index = %d, %d \n",result_index.x,result_index.y);
	//printf("Result size = %d, %d \n",Result.size.x,Result.size.y);
	return Result;
}

bool game_tile_rect_collision_new(Chunk * chunk_array[G::CHUNK_COUNT][G::CHUNK_COUNT], const Chunk * chunk, Position pos, V2i size)
{
	bool Result = false;
	V2i tile_index = pos.relative / v2i(G::GRAPHICS_TILE_SIZE);
	ChunkPortion test = get_nearby_tiles(chunk_array,chunk->id,tile_index,size);
	
	SDL_Rect our_rect = {pos.world.x,pos.world.y,size.x,size.y};
	//graphics_add_rect_to_stack(pos.world,v2i(G::GRAPHICS_TILE_SIZE),0,128,128,200);

	for (int i = 0; i < test.size.y; i++)
	{
		for(int j = 0; j < test.size.x; j++)
		{
			V2i other_world_pos = test.top_left_world_pos + 
									v2i(j * G::GRAPHICS_TILE_SIZE, i * G::GRAPHICS_TILE_SIZE);

			SDL_Rect other_rect = {other_world_pos.x, other_world_pos.y, 
										G::GRAPHICS_TILE_SIZE,G::GRAPHICS_TILE_SIZE};

			//graphics_add_rect_to_stack(other_world_pos,v2i(G::GRAPHICS_TILE_SIZE),0,128,0,128);
			if (test.flags[i][j] & TILE_COLLISION)
			{
				SDL_Rect out_collision = {0,0,0,0};
				SDL_bool collision = SDL_IntersectRect(&other_rect,&our_rect,&out_collision);
				if (collision){
				//	graphics_add_rect_to_stack(other_world_pos,v2i(G::GRAPHICS_TILE_SIZE),255,0,0,200);
				//	graphics_add_rect_to_stack(v2i(out_collision.x, out_collision.y),
				//		v2i(G::GRAPHICS_TILE_SIZE)*0.5,255,255,0,200);
					Result = true;
					//break;
				}
			}
		}
	}
	return Result;
}

bool Xgame_tile_rect_collision(Chunk * chunk_array[G::CHUNK_COUNT][G::CHUNK_COUNT], const Chunk * chunk, Position pos, V2i size, V2 & out_col_point, bool & found_flag, int flags)
{
	out_col_point.x = 0;
	out_col_point.y = 0;

	found_flag = true;

	V2i tile_index = pos.relative / v2i(G::GRAPHICS_TILE_SIZE);

	//ChunkPortion test = get_nearby_tiles(chunk_array,chunk->id,tile_index,size);
	
	game_tile_rect_collision_new(chunk_array,chunk, pos, size);
	
	
	const Chunk * chunkPtr = chunk;
	V2i chunk_offset=  {0,0};
	V2i chunk_id = chunkPtr->id;

	if (pos.relative.x + size.x >= G::CHUNK_SIZE_PIXELS){return true;}//{chunk_offset.x++;}
	if (pos.relative.x < 0.0f){return true;}//{chunk_offset.x--;}
	if (pos.relative.y + size.y >= G::CHUNK_SIZE_PIXELS){return true;}//chunk_offset.y++;}
	if (pos.relative.y < 0.0f){return true;}//{chunk_offset.y--;}

	V2i span_chunk = chunk_id + chunk_offset;

	//should this code be here?
	if (span_chunk.x < 0){span_chunk.x = 0;}
	if (span_chunk.y < 0){span_chunk.y = 0;}
	if (span_chunk.x >=  G::CHUNK_COUNT ){span_chunk.x =  G::CHUNK_COUNT -1;}
	if (span_chunk.y >=  G::CHUNK_COUNT ){span_chunk.y =  G::CHUNK_COUNT -1;}
	//

	if(!(chunk_offset == v2i(0,0)))
	{
		chunkPtr = game_get_chunk(chunk_array,  span_chunk);

		//this part is hacky
		//rel_pos = rel_pos - chunk_offset * G::CHUNK_SIZE_PIXELS;

		pos.relative.x = (int)pos.relative.x % (G::CHUNK_SIZE_PIXELS);
		pos.relative.y = (int)pos.relative.y % (G::CHUNK_SIZE_PIXELS);

	}

	assert(pos.relative.x <= G::CHUNK_SIZE_PIXELS);
	assert(pos.relative.y <= G::CHUNK_SIZE_PIXELS);
	SDL_Rect test_rect = {(int)pos.relative.x,(int)pos.relative.y,(int)size.x,(int)size.y};
	SDL_Rect tile_rect = {};
	V2 average_col_point = {};
	int col_count = 0;
	bool Result =false;
	int xmin = ((pos.relative.x)/G::GRAPHICS_TILE_SIZE) - 1;
	int ymin = ((pos.relative.y)/G::GRAPHICS_TILE_SIZE) - 1;				
	int xmax = ((pos.relative.x + size.x)/G::GRAPHICS_TILE_SIZE) + 1;
	int ymax = ((pos.relative.y + size.y)/G::GRAPHICS_TILE_SIZE) + 1;

	if (xmin < 0){xmin = 0;}
	if (ymin < 0){ymin = 0;}

	int color_rand = 0;
	V2i index = {0,0};
	for (index.x = xmin; index.x < xmax; index.x++)	{
		for (index.y = ymin; index.y < ymax; index.y++){		//changed from 0 to ymin...
			if (IsSet(chunkPtr, index,TILE_COLLISION)){

				//! fix this later !//
				if(index.x >= G::TILES_IN_CHUNK || index.x <0 ){continue;}
				if(index.y >= G::TILES_IN_CHUNK || index.y <0 ){continue;}
				assert(index.x<G::TILES_IN_CHUNK);
				assert(index.y<G::TILES_IN_CHUNK);

				bool flag_check = false;
				if ((flags != 0) && (IsSet(chunkPtr, index,flags))){
					flag_check = true;
				}

				tile_rect.x = index.x * G::GRAPHICS_TILE_SIZE;
				tile_rect.y = index.y * G::GRAPHICS_TILE_SIZE;
				tile_rect.w = G::GRAPHICS_TILE_SIZE;
				tile_rect.h = G::GRAPHICS_TILE_SIZE;

				color_rand+= (gc_iter++);
				//graphics_add_rect_to_stack(Xgame_get_abs_pos(chunk->id,
				//	v2(tile_rect.x, tile_rect.y)),v2(G::GRAPHICS_TILE_SIZE-6),
				//	color_rand%255,128,0,128);

				SDL_Rect retRect = {0,0,0,0};
				if (SDL_IntersectRect(&test_rect,&tile_rect,&retRect) == SDL_TRUE)
				{
					col_count++;
					average_col_point.x += retRect.x + 0.5f*retRect.w;
					average_col_point.y += retRect.y + 0.5f*retRect.h;
					if (flag_check){if(found_flag){	found_flag = true;}}
					Result = true;
				}
			}
		}
	}

	average_col_point.x /= col_count;
	average_col_point.y /= col_count;

	if (Result)
	{
		out_col_point.x = average_col_point.x;
		out_col_point.y = average_col_point.y;
	}
	
	
	//bool Result = false;
	return Result;
}

void validate_chunk_id(V2i & chunk_id)
{
	math_cap_values(chunk_id,v2i(0), v2i(G::CHUNK_COUNT-1));
	//if (chunk_id.x < 0){chunk_id.x = 0;}
	//if (chunk_id.y < 0){chunk_id.y = 0;}
	//if (chunk_id.x >=  G::CHUNK_COUNT ){chunk_id.x =  G::CHUNK_COUNT -1;}
	//if (chunk_id.y >=  G::CHUNK_COUNT ){chunk_id.y =  G::CHUNK_COUNT -1;}
}

void validate_chunk_range(V2i & out_min, V2i & out_max)
{
	validate_chunk_id(out_min);
	validate_chunk_id(out_max);
}

void get_chunks_on_screen(V2 world_camera_pos, V2i & out_min_cid, V2i & out_max_cid, V2i & out_camera_cid)
{
	V2i camera_chunk_id = game_get_chunk_id(world_camera_pos);

	V2i min = camera_chunk_id;
	V2i max = min + v2i(G::CHUNKS_PER_SCREEN.x+1,G::CHUNKS_PER_SCREEN.y+1);

	assert(min.x <= max.x);                   
	assert(min.y <= max.y);

	validate_chunk_range(min,max);
	validate_chunk_id(camera_chunk_id);
	out_min_cid = min;
	out_max_cid = max;
	out_camera_cid = camera_chunk_id;

	assert((out_camera_cid.x >=0 && out_camera_cid.x <  G::CHUNK_COUNT ));
	assert(out_camera_cid.y >=0 && out_camera_cid.y <  G::CHUNK_COUNT );
	assert(out_min_cid.x>=0);
	assert(out_min_cid.y>=0);
	assert(out_max_cid.x< G::CHUNK_COUNT );
	assert(out_max_cid.y< G::CHUNK_COUNT );
}

MessageData game_chunk_on_entity_destroy(void * sender, std::string message, MessageData md)
{
	MessageData Result = {};
#if 0
	Entity * e = (Entity * )sender;
	Chunk ** chunk_array = (Chunk**)md.ptrs[0];
	game_get_chunk_unsafe(chunk_array,e->_pos_.chunk_index)->entitys->remove(e);
	LOG_ERROR("Entity removed from chunk after death \n");
#endif
	return Result;
}


MessageData game_chunk_on_entity_move(void * sender, std::string message, MessageData md)
{
	MessageData Result = {};
	
#if 0
	Entity * e = (Entity * )sender;
	Chunk ** chunk_array = (Chunk**)md.ptrs[0];
	V2i old_pos = v2i(md.ints[0],md.ints[1]);
	V2i new_pos = v2i(md.ints[2],md.ints[3]);

	if (old_pos.x != new_pos.x ||
		old_pos.y != new_pos.y)	
	{
		game_get_chunk_unsafe(chunk_array, old_pos)->entitys->remove(e);
		MessageRouter_SpawnTriggeredMessage(md.router,sender,"on_leave_chunk");

		game_get_chunk_unsafe(chunk_array, new_pos)->entitys->add(e);
		MessageRouter_SpawnTriggeredMessage(md.router,sender,"on_enter_chunk");
		
	}
#endif
	return Result;
}